home *** CD-ROM | disk | FTP | other *** search
/ Merciful 5 / Merciful - Disc 5.iso / software / p / pcqpascalv1.2d.lha / PCQ / PCQ.p < prev   
Text File  |  1991-09-19  |  43KB  |  1,836 lines

  1. Program PCQ;
  2.  
  3. {$I "Include:Utils/StringLib.i"}
  4. {$I "Include:Utils/Parameters.i"}
  5. {$I "Include:Libraries/DOS.i"}
  6. {$I "Include:Exec/Tasks.i"}
  7. {$I "Include:Libraries/DOSExtens.i"}
  8. {$I "Include:Utils/Break.i"}
  9.  
  10.  
  11. Type
  12.  
  13. {
  14.      The WordList type stores all sorts of lists of strings,
  15.      like the legal suffixes of Pascal source files or whatever.
  16.      A variable of type WordListPtr can be Nil.
  17. }
  18.  
  19.     WordListPtr = ^WordList;
  20.     WordList = record
  21.     Next    : WordListPtr;
  22.     Name    : String;
  23.     end;
  24.  
  25. {
  26.     This enumerated type spells out each available command.  Since
  27.     we use a simple binary search to look through the commands,
  28.     these must remain in alphabetical order.
  29. }
  30.  
  31.     KeyWord = (
  32.         key_a,
  33.         key_assemble,
  34.         key_assembler,
  35.         key_assemblererror,
  36.         key_assembly,
  37.         key_b,
  38.         key_c,
  39.         key_ccalls,
  40.         key_checkio,
  41.         key_compiler,
  42.         key_compilererror,
  43.         key_d,
  44.         key_debug,
  45.         key_destination,
  46.         key_discard,
  47.         key_exec,
  48.         key_execute,
  49.         key_external,
  50.         key_f,
  51.         key_force,
  52.         key_i,
  53.         key_l,
  54.         key_library,
  55.         key_link,
  56.         key_linker,
  57.         key_linkererror,
  58.         key_linkform,
  59.         key_main,
  60.         key_map,
  61.         key_mapoptions,
  62.         key_mathtrans,
  63.         key_n,
  64.         key_nodebug,
  65.         key_o,
  66.         key_object,
  67.         key_optimize,
  68.         key_optimizer,
  69.         key_optimizererror,
  70.         key_p,
  71.         key_pascal,
  72.         key_profiler,
  73.         key_project,
  74.         key_r,
  75.         key_range,
  76.         key_s,
  77.         key_shortcircuit,
  78.         key_smallcode,
  79.         key_smalldata,
  80.         key_smallinit,
  81.         key_source,
  82.         key_temp,
  83.         key_v,
  84.         key_verbose,
  85.         key_x,
  86.         key_xref,
  87.  
  88.         key_unknown
  89.         );
  90.  
  91. const
  92.  
  93. {
  94.     This typed constant gives the actual text for each
  95.     possible command.
  96. }
  97.  
  98.     Keywords : Array [key_a .. Pred(key_unknown)] of String = (
  99.         "a",
  100.         "assemble",
  101.         "assembler",
  102.         "assemblererror",
  103.         "assembly",
  104.         "b",
  105.         "c",
  106.         "ccalls",
  107.         "checkio",
  108.         "compiler",
  109.         "compilererror",
  110.         "d",
  111.         "debug",
  112.         "destination",
  113.         "discard",
  114.         "exec",
  115.         "execute",
  116.         "external",
  117.         "f",
  118.         "force",
  119.         "i",
  120.         "l",
  121.         "library",
  122.         "link",
  123.         "linker",
  124.         "linkererror",
  125.         "linkform",
  126.         "main",
  127.         "map",
  128.         "mapoptions",
  129.         "mathtrans",
  130.         "n",
  131.         "nodebug",
  132.         "o",
  133.         "object",
  134.         "optimize",
  135.         "optimizer",
  136.         "optimizererror",
  137.         "p",
  138.         "pascal",
  139.         "profiler",
  140.         "project",
  141.         "r",
  142.         "range",
  143.         "s",
  144.         "shortcircuit",
  145.         "smallcode",
  146.         "smalldata",
  147.         "smallinit",
  148.         "source",
  149.         "temp",
  150.         "v",
  151.         "verbose",
  152.         "x",
  153.         "xref");
  154.  
  155. var
  156.  
  157. { The current command line parameter being processed }
  158.  
  159.     ParamNumber        : Integer;
  160.  
  161. { The text of that command line parameter }
  162.  
  163.     Parameter        : String;
  164.  
  165. { Where the current command is coming from }
  166.  
  167.     CommandSource    : (src_commandline, src_cfg, src_project);
  168.  
  169. { Which type of file is being processed }
  170.  
  171.     FileClass        : (class_unknown, class_main, class_external);
  172.  
  173. { A number for creating unique file names }
  174.  
  175.     Unique        : Integer;
  176.  
  177. { The general purpose output file }
  178.  
  179.     Handle        : FileHandle;
  180.  
  181.  
  182. { Options set in .CFG or .PROJECT files }
  183.  
  184.     { Path options }
  185.  
  186.     Compiler_path    : String;    { Command string for the compiler }
  187.     Assembler_path    : String;    { Command string for the assembler }
  188.     Linker_path        : String;    { Command string for the linker }
  189.     Optimizer_path    : String;    { Command string for the optimizer }
  190.     Source_path        : String;    { Directory for source files }
  191.     Dest_path        : String;    { Directory to leave files in }
  192.     Temp_path        : String;    { Directory for temporary files }
  193.  
  194.     CompError_name    : String;    { template for compiler error handler }
  195.     OptError_name    : String;    { template for optimizer error handler }
  196.     AssemError_name    : String;    { template for assembler error handler }
  197.     LinkError_name    : String;    { template for linker error handler }
  198.  
  199.     Exec_name        : String;    { Name of executable }
  200.     Main_name        : String;    { Name of main module }
  201.     Map_name        : String;    { Name of map file }
  202.     Xref_name        : String;    { Name of XREF file }
  203.  
  204.     External_names    : WordListPtr;    { Name of external files }
  205.  
  206.     { Suffixes }
  207.  
  208.     Project_suffix    : WordListPtr;    { Suffixes for Project files }
  209.     Pascal_suffix    : WordListPtr;    { Suffixes for Pascal source files }
  210.     Assembly_suffix    : WordListPtr;    { Suffixes for Assembly source files }
  211.     Object_suffix    : WordListPtr;    { Suffixes for Object files }
  212.  
  213.     { PCQ options }
  214.  
  215.     opt_Verbose        : Boolean;    { TRUE => Should write messages }
  216.  
  217.     { Compiler options }
  218.  
  219.     opt_SmallInit    : Boolean;    { TRUE => specify -s option }
  220.     opt_Profiler    : Boolean;    { TRUE => specify -p option }
  221.     opt_Shortcircuit    : Boolean;    { FALSE => specify -$B option }
  222.     opt_CheckIO        : Boolean;    { FALSE => specify -$B option }
  223.     opt_MathTrans    : Boolean;    { TRUE => specify +$N option }
  224.     opt_RangeCheck    : Boolean;    { TRUE => specify +$R option }
  225.     opt_Force        : Boolean;    { TRUE => ignore time diffs }
  226.     opt_CCalls        : Boolean;    { TRUE => use C calling conventions }
  227.     opt_Discard        : Boolean;    { TRUE => use functions as procedures }
  228.  
  229.     { Optimizer options }
  230.  
  231.     opt_Optimize    : Boolean;    { TRUE => run peephole optimizer }
  232.  
  233.     { Assembler options }
  234.  
  235.     opt_Debug        : Boolean;    { TRUE => specify -d option }
  236.     opt_Assemble    : Boolean;    { TRUE => run assembler }
  237.  
  238.     { Linker options }
  239.  
  240.     opt_NoDebug        : Boolean;    { TRUE => specify NODEBUG }
  241.     opt_SmallCode    : Boolean;    { TRUE => specify SMALLCODE }
  242.     opt_SmallData    : Boolean;    { TRUE => specify SMALLDATA }
  243.     opt_Map        : Boolean;    { TRUE => specify MAP }
  244.     opt_Xref        : Boolean;    { TRUE => generate XREF }
  245.     opt_Link        : Boolean;    { TRUE => run the linker }
  246.     MapOptions        : String;    { passsed to Blink }
  247.     LinkForm        : (form_Alink,form_Blink,form_Dlink);
  248.                     { the linker to be used }
  249.  
  250.     LibraryList        : WordListPtr;    { List of libraries to send to linker }
  251.  
  252.  
  253. {   This function returns TRUE if the given file exists }
  254.  
  255. Function ExistFile(Name : String) : Boolean;
  256. var
  257.     MyLock : FileLock;
  258. begin
  259.     MyLock := Lock(Name, SHARED_LOCK);
  260.     if MyLock <> Nil then begin
  261.     Unlock(MyLock);
  262.     ExistFile := True
  263.     end else
  264.     ExistFile := False;
  265. end;
  266.  
  267.  
  268. { This routine deletes Num characters from Str, starting at Pos }
  269.  
  270. Procedure strdel(Str : String; Pos, Num : Short);
  271. var
  272.     i : Short;
  273. begin
  274.     for i := Pos to strlen(Str) - Num do
  275.     Str[i] := Str[i + Num];
  276. end;
  277.  
  278.  
  279. {
  280.   This routine inserts the string Insertion into the string Str,
  281.   beginning at location Pos
  282. }
  283.  
  284. Procedure strins(Str, Insertion : String; Pos : Short);
  285. var
  286.     i : Short;
  287.     len : Short;
  288. begin
  289.     if strlen(Str) <= Pos then
  290.     strcat(Str, Insertion)
  291.     else begin
  292.     len := strlen(Insertion);
  293.     for i := strlen(Str) downto Pos do
  294.         Str[i + Len] := Str[i];
  295.     for i := Pos to Pred(Pos + Len) do
  296.         Str[i] := Insertion[i - Pos];
  297.     end;
  298. end;
  299.  
  300. { This routine adds a new word to a word list }
  301.  
  302. Procedure AddWord(VAR WList : WordListPtr; NewWord : String);
  303. var
  304.     TempWord : WordListPtr;
  305. begin
  306.     New(TempWord);
  307.     with TempWord^ do begin
  308.     Next := WList;
  309.     Name := strdup(NewWord);
  310.     end;
  311.     WList := TempWord;
  312. end;
  313.  
  314.  
  315. { This routine aborts the program with an error message }
  316.  
  317. Procedure Abort(Error : String);
  318. begin
  319.     Writeln(Error);
  320.     if Handle <> Nil then
  321.     DOSClose(Handle);
  322.     Exit(20);
  323. end;
  324.  
  325.  
  326. (*
  327.  
  328. { This is a debugging routine that displays a word list }
  329.  
  330. Procedure WriteWordList(Prefix : String; WL : WordListPtr);
  331. begin
  332.     Write(Prefix);
  333.     while WL <> Nil do begin
  334.     Write(WL^.Name);
  335.     WL := WL^.Next;
  336.     if WL <> Nil then
  337.         Write(',');
  338.     end;
  339.     Writeln;
  340. end;
  341.  
  342.  
  343. { This is a debugging routine that displays all the options }
  344.  
  345. Procedure ShowOptions;
  346. begin
  347.  
  348.     Writeln('Compiler     ', Compiler_path);
  349.     Writeln('Assembler    ', Assembler_path);
  350.     Writeln('Linker       ', Linker_path);
  351.     Writeln('Optimizer    ', Optimizer_path);
  352.     Writeln('Source dir   ', Source_path);
  353.     Writeln('Destination  ', Dest_path);
  354.     Writeln('Temporary    ', Temp_path);
  355.  
  356.     Writeln('Executable   ', Exec_name);
  357.     Writeln('Map file     ', Map_name);
  358.     Writeln('Xref file    ', Xref_name);
  359.  
  360.     { Suffixes }
  361.  
  362.     WriteWordList("Projects:   ", Project_suffix);
  363.     WriteWordList("Pascal:     ", Pascal_suffix);
  364.     WriteWordList("Assembly:   ", Assembly_suffix);
  365.     WriteWordList("Object:     ", Object_suffix);
  366.  
  367.     { Compiler options }
  368.  
  369.     Writeln('SmallInit    ', opt_SmallInit);
  370.     Writeln('Profiler     ', opt_Profiler);
  371.     Writeln('ShortCircuit ', opt_Shortcircuit);
  372.     Writeln('CheckIO      ', opt_CheckIO);
  373.     Writeln('MathTrans    ', opt_MathTrans);
  374.     Writeln('Range Check  ', opt_RangeCheck);
  375.  
  376.     { Optimizer options }
  377.  
  378.     Writeln('Optimize     ', opt_Optimize);
  379.  
  380.     { Assembler options }
  381.  
  382.     Writeln('Debug        ', opt_Debug);
  383.     Writeln('Assemble     ', opt_Assemble);
  384.  
  385.     { Linker options }
  386.  
  387.     Writeln('NODEBUG      ', opt_NoDebug);
  388.     Writeln('SMALLCODE    ', opt_SmallCode);
  389.     Writeln('SMALLDATA    ', opt_SmallData);
  390.     Writeln('MAP          ', opt_Map);
  391.     Writeln('Link         ', opt_Link);
  392.     Writeln('XREF         ', opt_Xref);
  393.     Writeln('Map options  "', MapOptions, '"');
  394.  
  395.     WriteWordList("Libraries   ", LibraryList);
  396. end;
  397. *)
  398.  
  399. { This routine sets up all the options with default values }
  400.  
  401. Procedure SetDefaults;
  402. begin
  403.     { Paths }
  404.  
  405.     Compiler_path    := strdup("Pascal");
  406.     Assembler_path    := strdup("A68k");
  407.     Linker_path        := strdup("Blink");
  408.     Optimizer_path    := strdup("Peep");
  409.     Source_path        := strdup("");
  410.     Dest_path        := strdup("");
  411.     Temp_path        := strdup("T:");
  412.  
  413.     CompError_name    := strdup("CompErrors \\s \\d \\e");
  414.     OptError_name    := strdup("OptErrors \\s \\e");
  415.     AssemError_name    := strdup("AssemErrors \\s \\e");
  416.     LinkError_name    := strdup("LinkErrors \\e");
  417.  
  418.     Exec_name        := strdup("");
  419.     Main_name        := strdup("");
  420.     Map_name        := strdup("");
  421.     Xref_name        := strdup("");
  422.     External_names    := Nil;
  423.  
  424.     { Suffixes }
  425.  
  426.     Project_suffix := Nil;
  427.     AddWord(Project_suffix, ".project");
  428.  
  429.     Pascal_suffix := Nil;
  430.     AddWord(Pascal_suffix, ".p");
  431.  
  432.     Assembly_suffix := Nil;
  433.     AddWord(Assembly_suffix, ".asm");
  434.  
  435.     Object_suffix := Nil;
  436.     AddWord(Object_suffix, ".o");
  437.  
  438.     { PCQ options }
  439.  
  440.     opt_Verbose        := True;
  441.  
  442.     { Compiler options }
  443.  
  444.     opt_SmallInit    := False;
  445.     opt_Profiler    := False;
  446.     opt_Shortcircuit    := True;
  447.     opt_CheckIO        := True;
  448.     opt_MathTrans    := False;
  449.     opt_RangeCheck    := False;
  450.     opt_Force        := False;
  451.     opt_CCalls        := False;
  452.     opt_Discard        := False;
  453.  
  454.     { Optimizer options }
  455.  
  456.     opt_Optimize    := True;
  457.  
  458.     { Assembler options }
  459.  
  460.     opt_Debug        := False;
  461.     opt_Assemble    := True;
  462.  
  463.     { Linker options }
  464.  
  465.     opt_NoDebug        := True;
  466.     opt_SmallCode    := False;
  467.     opt_SmallData    := False;
  468.     opt_Map        := False;
  469.     opt_Xref        := False;
  470.     opt_Link        := True;
  471.     LinkForm        := form_Blink;
  472.     MapOptions        := strdup("S PLAIN");
  473.  
  474.     LibraryList := Nil;
  475.     AddWord(LibraryList, "PCQ.lib");
  476.  
  477.     FileClass           := class_unknown;
  478. end;
  479.  
  480.  
  481. {
  482.   This routine takes an InputLine and points the string Command
  483.   to the first word in the line, and Suffix to the first of the
  484.   remaining words.  No new strings are allocated, and Command
  485.   and Suffix both point to memory inside the original string.
  486. }
  487.  
  488. Procedure SplitLine(InputLine : String;
  489.             VAR Command, Suffix : String);
  490. var
  491.     i : Integer;
  492. begin
  493.     i := 0;
  494.  
  495.     while (InputLine[i] <> '\0') and (InputLine[i] <= ' ') do
  496.     Inc(i);
  497.  
  498.     Command := String(@InputLine[i]);
  499.  
  500.     while InputLine[i] > ' ' do
  501.     Inc(i);
  502.  
  503.     if InputLine[i] <> '\0' then begin
  504.     InputLine[i] := '\0';
  505.     Inc(i);
  506.     while (InputLine[i] <> '\0') and (InputLine[i] <= ' ') do
  507.         Inc(i);
  508.     end;
  509.     Suffix := String(@InputLine[i]);
  510. end;
  511.  
  512.  
  513. {
  514.     This routine figures out which of the options corresponds to
  515.     the given command word, using a binary search.
  516. }
  517.  
  518. Function FindOption(Option : String) : Keyword;
  519. var
  520.     top,
  521.     middle,
  522.     bottom    : Short;
  523.     compare    : Short;
  524. begin
  525.     Bottom := Ord(key_a);
  526.     Top    := Ord(Pred(key_unknown));
  527.     while Bottom <= Top do begin
  528.     middle := (bottom + top) div 2;
  529.     Compare := stricmp(Keywords[Keyword(Middle)], Option);
  530.     if Compare = 0 then
  531.         FindOption := Keyword(Middle)
  532.     else if Compare < 0 then
  533.         Bottom := Succ(Middle)
  534.     else
  535.         Top := Pred(Middle);
  536.     end;
  537.     FindOption := key_unknown;
  538. end;
  539.  
  540. {
  541.     This routine sets a Boolean value based on a switch
  542.     character
  543. }
  544.  
  545. Procedure SetSwitch(VAR Switch : Boolean; Value : Char);
  546. begin
  547.     case Value of
  548.      '-' : Switch := False;
  549.      '+' : Switch := True;
  550.     else
  551.     Abort("Unknown command");
  552.     end;
  553. end;
  554.  
  555. {    This routine sets a command path }
  556.  
  557. Procedure SetCommandPath(VAR Path : String; NewPath : String);
  558. begin
  559.     FreeString(Path);
  560.     Path := strdup(NewPath);
  561. end;
  562.  
  563.  
  564. {
  565.   This routine either adds a new word to a word list, or if the
  566.   word is blank (i.e. ""), it clears the word list.
  567. }
  568.  
  569. Procedure SetList(VAR WL : WordListPtr; NewWord : String);
  570. var
  571.     Temp : WordListPtr;
  572. begin
  573.     if NewWord^ = Chr(0) then begin
  574.     while WL <> Nil do begin
  575.         Temp := WL^.Next;
  576.         Dispose(WL);
  577.         WL := Temp;
  578.     end;
  579.     end else
  580.     AddWord(WL, NewWord);
  581. end;
  582.  
  583. {
  584.     This routine sets a directory name, appending a slash
  585.     character if necessary.
  586. }
  587.  
  588. Procedure SetPath(VAR Path : String; NewPath : String);
  589. var
  590.     LastChar : Char;
  591. begin
  592.     FreeString(Path);
  593.     Path := AllocString(strlen(NewPath) + 2);
  594.     strcpy(Path, NewPath);
  595.     if strlen(Path) > 0 then begin
  596.     LastChar := Path[Pred(strlen(Path))];
  597.     if (LastChar <> '/') and (LastChar <> ':') then
  598.         strcat(Path, "/");
  599.     end;
  600. end;
  601.  
  602. {
  603.     This routine sets a string variable to the given name
  604. }
  605.  
  606. Procedure SetName(VAR Name : String; NewName : String);
  607. begin
  608.     FreeString(Name);
  609.     Name := strdup(NewName);
  610. end;
  611.  
  612. {
  613.     This routine sets the map name, as well as whether or not
  614.     a map file will be generated.
  615. }
  616.  
  617. Procedure SetMapName(Flag : Char; NewName : String);
  618. begin
  619.     if Flag = '+' then begin
  620.     opt_Map := True;
  621.     if strlen(NewName) > 0 then
  622.         SetName(Map_name, NewName);
  623.     end else if Flag = '-' then
  624.     opt_Map := False;
  625. end;
  626.  
  627. Procedure SetXrefName(Flag : Char; NewName : String);
  628. begin
  629.     if Flag = '+' then begin
  630.     opt_Xref := True;
  631.     if strlen(NewName) > 0 then
  632.         SetName(Xref_name, NewName);
  633.     end else if Flag = '-' then
  634.     opt_Xref := False;
  635. end;
  636.  
  637.  
  638. {
  639.     This routine determines whether the string Suffix is a
  640.     suffix of Str.
  641. }
  642.  
  643. Function IsSuffix(Str, Suffix : String) : Boolean;
  644. var
  645.     Len1, Len2 : Integer;
  646. begin
  647.     Len1 := strlen(Str);
  648.     Len2 := strlen(Suffix);
  649.     if Len1 < Len2 then
  650.     IsSuffix := False;
  651.     IsSuffix := strieq(String(@Str[Len1 - Len2]), Suffix);
  652. end;
  653.  
  654.  
  655. {
  656.     This function determines whether any of the suffixes in a
  657.     list is the suffix of Name.  If it exists, it returns a
  658.     pointer to the word record, or Nil if the suffix is not there.
  659. }
  660.  
  661. Function HasSuffix(Name : String; WL : WordListPtr) : WordListPtr;
  662. begin
  663.     while WL <> Nil do begin
  664.     if issuffix(Name,WL^.Name) then
  665.         HasSuffix := WL;
  666.     WL := WL^.Next;
  667.     end;
  668.     HasSuffix := Nil;
  669. end;
  670.  
  671. {
  672.     This function returns a file's date and time as the number of
  673.     seconds after January 1, 1978 that the file was created.
  674. }
  675.  
  676. Function FileTime(FName : String) : Integer;
  677. var
  678.     FInfo : FileInfoBlockPtr;
  679.     FTime : Integer;
  680.     FLock : FileLock;
  681. begin
  682.     FLock := Lock(FName, SHARED_LOCK);
  683.     if FLock = Nil then
  684.     FileTime := 0;
  685.  
  686.     New(FInfo);
  687.     if Examine(FLock, FInfo) then begin
  688.     with FInfo^.fib_Date do
  689.         FTime := ds_Days * (24 * 60 * 60) +
  690.              ds_Minute * 60 +
  691.              ds_Tick div 50;
  692.     end else
  693.     FTime := 0;
  694.  
  695.     Unlock(FLock);
  696.     Dispose(FInfo);
  697.  
  698.     FileTime := FTime;
  699. end;
  700.  
  701. {
  702.     This function returns a file's size
  703. }
  704.  
  705. Function FileSize(FileName : String) : Integer;
  706. var
  707.     FInfo : FileInfoBlockPtr;
  708.     FLock : FileLock;
  709.     FSize : Integer;
  710. begin
  711.     FLock := Lock(FileName,SHARED_LOCK);
  712.     if FLock = Nil then
  713.     FileSize := 0
  714.     else begin
  715.     New(FInfo);
  716.     if Examine(FLock, FInfo) then
  717.         FSize := FInfo^.fib_Size
  718.     else
  719.         FSize := 0;
  720.     Dispose(FInfo);
  721.     Unlock(FLock);
  722.     FileSize := FSize;
  723.     end;
  724. end;
  725.  
  726. {
  727.     This function returns in Result the file name from FullPath,
  728.     with any directory and volume information stripped off.  Result
  729.     must already be allocated.
  730. }
  731.  
  732. Procedure OnlyFileName(FullPath, Result : String);
  733. var
  734.    i,j : Integer;
  735. begin
  736.     strcpy(Result, FullPath);
  737.     i := strrpos(Result, '/');
  738.     j := strrpos(Result, ':');
  739.     if i < j then
  740.     i := j;
  741.     if i <> 0 then
  742.     strdel(Result, 0, Succ(i));
  743. end;
  744.  
  745. {
  746.     This function strips the suffix off the Str
  747. }
  748.  
  749. Procedure StripSuffix(Str, Suffix : String);
  750. begin
  751.     Str[strlen(Str) - strlen(Suffix)] := '\0';
  752. end;
  753.  
  754. {
  755.     This function concatenates a parameter onto a command string,
  756.     first appending a space.
  757. }
  758.  
  759. Procedure CatParam(Dest, Param : String);
  760. begin
  761.     strcat(Dest, " ");
  762.     strcat(Dest, Param);
  763. end;
  764.  
  765. {
  766.     This routine finds an unused file name with the appropriate suffix
  767. }
  768.  
  769. Procedure GetTempFileName(Name, Suffix : String);
  770. var
  771.     Dummy  : Integer;
  772.     Temp   : Array [0..19] of Char;
  773. begin
  774.     repeat
  775.     Dummy := IntToStr(String(@Temp), Unique);
  776.     strcpy(Name, Temp_path);
  777.     strcat(Name, "PCQ_Temp");
  778.     strcat(Name, String(@Temp));
  779.     strcat(Name, Suffix);
  780.     Inc(Unique);
  781.     until not ExistFile(Name);
  782. end;
  783.  
  784.  
  785. {
  786.     This routine executes the program, aborting if there are any
  787.     errors.
  788. }
  789.  
  790. Procedure ExecuteProgram(Command : String);
  791. var
  792.     Success : Boolean;
  793. begin
  794.     if CheckBreak then
  795.     Abort("User Aborted");
  796.     Success := Execute(Command, Nil, Handle);
  797.     if not Success then
  798.     Abort("Could not execute program");
  799. end;
  800.  
  801.  
  802.  
  803. {
  804.     This routine executes an error routine
  805. }
  806.  
  807. Procedure ProcessError(Template, FromName, ToName, ErrorFile : String);
  808. var
  809.     ErrorCommand : String;
  810.     i         : Integer;
  811. begin
  812.     ErrorCommand := AllocString(256);
  813.     strcpy(ErrorCommand, Template);
  814.     i := 0;
  815.     while ErrorCommand[i] <> '\0' do begin
  816.     if ErrorCommand[i] = '\\' then begin
  817.         Inc(i);
  818.         case toupper(ErrorCommand[i]) of
  819.           'S' : begin
  820.             strdel(ErrorCommand,Pred(i),2);
  821.             strins(ErrorCommand,FromName,Pred(i));
  822.             Inc(i,Pred(strlen(FromName)));
  823.             end;
  824.           'D' : begin
  825.             strdel(ErrorCommand,Pred(i),2);
  826.             strins(ErrorCommand,ToName,Pred(i));
  827.             Inc(i,Pred(strlen(ToName)));
  828.             end;
  829.           'E' : begin
  830.             strdel(ErrorCommand,Pred(i),2);
  831.             strins(ErrorCommand,ErrorFile,Pred(i));
  832.             Inc(i,Pred(strlen(ErrorFile)));
  833.             end;
  834.         else
  835.         Inc(i);
  836.         end;
  837.     end else
  838.         Inc(i);
  839.     end;
  840.  
  841.     if opt_Verbose then
  842.     Writeln(ErrorCommand);
  843.  
  844.     ExecuteProgram(ErrorCommand);
  845.     Abort("PCQ found errors");
  846. end;
  847.  
  848.  
  849. {
  850.     This routine compiles the file FromName.  It figures out an
  851.     appropriate ToName, and copies it into ToName (which must
  852.     already be allocated).
  853.  
  854.     If there are any errors, this routine executes the compiler
  855.     error command string.
  856. }
  857.  
  858. Procedure Compile(FromName, BaseName, ToName : String);
  859. var
  860.     Command : String;
  861.     ErrorFile : String;
  862. begin
  863.     Command   := AllocString(256);
  864.     ErrorFile := AllocString(256);
  865.  
  866.     GetTempFileName(ErrorFile, "");
  867.  
  868.     if opt_Optimize or opt_Assemble then begin
  869.     if Assembly_suffix <> Nil then
  870.         GetTempFileName(ToName, Assembly_suffix^.Name)
  871.     else
  872.         GetTempFileName(ToName, ".asm");
  873.     end else begin
  874.     strcpy(ToName, Dest_path);
  875.     strcat(ToName, BaseName);
  876.     if Assembly_suffix <> Nil then
  877.         strcat(ToName, Assembly_suffix^.Name)
  878.     else
  879.         strcat(ToName, ".asm");
  880.     end;
  881.  
  882.     Handle := DOSOpen(ErrorFile, MODE_NEWFILE);
  883.     if Handle = Nil then
  884.     Abort("Could not open temporary file");
  885.  
  886.     strcpy(Command, Compiler_path);
  887.     CatParam(Command, FromName);
  888.     CatParam(Command, ToName);
  889.     strcat(Command, " -q");
  890.  
  891.     if opt_SmallInit then
  892.     strcat(Command, " -s");
  893.     if opt_Profiler then
  894.     strcat(Command, " -p");
  895.     if not opt_ShortCircuit then
  896.     strcat(Command, " -$B");
  897.     if not opt_CheckIO then
  898.     strcat(Command, " -$I");
  899.     if opt_MathTrans then
  900.     strcat(Command, " +$N");
  901.     if opt_RangeCheck then
  902.     strcat(Command, " +$R");
  903.     if opt_CCalls then
  904.     strcat(Command, " +$C");
  905.     if opt_Discard then
  906.     strcat(Command, " +$X");
  907.  
  908.     if opt_Verbose then
  909.     Writeln(Command);
  910.  
  911.     ExecuteProgram(Command);
  912.  
  913.     DOSClose(Handle);
  914.     Handle := Nil;
  915.  
  916.     if FileSize(ErrorFile) > 0 then
  917.     ProcessError(CompError_name, FromName, ToName, ErrorFile);
  918.  
  919.     if not DeleteFile(ErrorFile) then
  920.     Abort("Could not delete temporary file");
  921.  
  922.     FreeString(Command);
  923.     FreeString(ErrorFile);
  924. end;
  925.  
  926. {
  927.     This routine optimizes FromName.  It figures out the destination
  928.     name, and copies it to ToName, which must already be initialized.
  929. }
  930.  
  931. Procedure Optimize(FromName, BaseName, ToName : String);
  932. var
  933.     Command : String;
  934.     ErrorFile : String;
  935. begin
  936.     Command   := AllocString(256);
  937.     ErrorFile := AllocString(256);
  938.  
  939.     GetTempFileName(ErrorFile, "");
  940.  
  941.     if opt_Assemble then begin
  942.     if Assembly_suffix <> Nil then
  943.         GetTempFileName(ToName, Assembly_suffix^.Name)
  944.     else
  945.         GetTempFileName(ToName, ".s");
  946.     end else begin
  947.     strcpy(ToName, Dest_path);
  948.     strcat(ToName, BaseName);
  949.     if Assembly_suffix <> Nil then
  950.         strcat(ToName, Assembly_suffix^.Name)
  951.     else
  952.         strcat(ToName, ".s");
  953.     end;
  954.  
  955.     Handle := DOSOpen(ErrorFile, MODE_NEWFILE);
  956.     if Handle = Nil then
  957.     Abort("Could not create temporary file");
  958.  
  959.     strcpy(Command, Optimizer_path);
  960.     CatParam(Command, FromName);
  961.     CatParam(Command, ToName);
  962.  
  963.     if opt_Verbose then
  964.     Writeln(Command);
  965.  
  966.     ExecuteProgram(Command);
  967.  
  968.     DOSClose(Handle);
  969.     Handle := Nil;
  970.  
  971.     if FileSize(ErrorFile) > 0 then
  972.     ProcessError(OptError_name, FromName, ToName, ErrorFile);
  973.  
  974.     if not DeleteFile(ErrorFile) then
  975.     Abort("Could not delete temporary file");
  976.  
  977.     FreeString(Command);
  978.     FreeString(ErrorFile);
  979. end;
  980.  
  981. Procedure Assemble(FromName, BaseName, ToName : String);
  982. var
  983.     Command : String;
  984.     ErrorFile : String;
  985. begin
  986.     Command   := AllocString(256);
  987.     ErrorFile := AllocString(256);
  988.  
  989.     GetTempFileName(ErrorFile, "");
  990.  
  991.     if opt_Link and (CommandSource = src_CommandLine) then begin
  992.     if Object_suffix <> Nil then
  993.         GetTempFileName(ToName, Object_suffix^.Name)
  994.     else
  995.         GetTempFileName(ToName, ".o");
  996.     end else begin
  997.     strcpy(ToName, Dest_path);
  998.     strcat(ToName, BaseName);
  999.     if Object_suffix <> Nil then
  1000.         strcat(ToName, Object_suffix^.Name)
  1001.     else
  1002.         strcat(ToName, ".o");
  1003.     end;
  1004.  
  1005.     Handle := DOSOpen(ErrorFile, MODE_NEWFILE);
  1006.     if Handle = Nil then
  1007.     Abort("Could not open temporary file");
  1008.  
  1009.     strcpy(Command, Assembler_path);
  1010.     CatParam(Command, FromName);
  1011.     CatParam(Command, ToName);
  1012.     strcat(Command, " -q");
  1013.  
  1014.     if opt_Debug then
  1015.     strcat(Command, " -d");
  1016.  
  1017.     if opt_Verbose then
  1018.     Writeln(Command);
  1019.  
  1020.     ExecuteProgram(Command);
  1021.  
  1022.     DOSClose(Handle);
  1023.     Handle := Nil;
  1024.  
  1025.     if not ExistFile(ToName) then
  1026.     ProcessError(AssemError_name, FromName, ToName, ErrorFile);
  1027.  
  1028.     if not DeleteFile(ErrorFile) then
  1029.     Abort("Could not delete temporary file");
  1030.  
  1031.     FreeString(Command);
  1032.     FreeString(ErrorFile);
  1033. end;
  1034.  
  1035.  
  1036. {
  1037.     Link a single file
  1038. }
  1039.  
  1040. Procedure Link(FromName, BaseName, ToName : String);
  1041. var
  1042.     Command : String;
  1043.     ErrorFile : String;
  1044.     Lib          : WordListPtr;
  1045.     Errors    : Text;
  1046. begin
  1047.     Command   := AllocString(256);
  1048.     ErrorFile := AllocString(256);
  1049.  
  1050.     GetTempFileName(ErrorFile, "");
  1051.  
  1052.     strcpy(ToName, Dest_path);
  1053.     strcat(ToName, BaseName);
  1054.  
  1055.     Handle := DOSOpen(ErrorFile, MODE_NEWFILE);
  1056.     if Handle = Nil then
  1057.     Abort("Could not open temporary file");
  1058.  
  1059.     strcpy(Command, Linker_path);
  1060.     CatParam(Command, FromName);
  1061.     if LinkForm <> form_Dlink then
  1062.     strcat(Command, " TO ")
  1063.     else
  1064.     strcat(Command, " -o ");
  1065.     strcat(Command, ToName);
  1066.  
  1067.     if LibraryList <> Nil then begin
  1068.     if LinkForm <> form_Dlink then
  1069.         strcat(Command, " LIBRARY");
  1070.     Lib := LibraryList;
  1071.     while Lib <> Nil do begin
  1072.         CatParam(Command, Lib^.Name);
  1073.         Lib := Lib^.Next;
  1074.     end;
  1075.     end;
  1076.  
  1077.     if opt_NoDebug then begin
  1078.     if LinkForm = form_Blink then
  1079.         strcat(Command, " NODEBUG");
  1080.     end else if LinkForm = form_Dlink then
  1081.     strcat(Command, " -s");
  1082.  
  1083.     if opt_Map and (LinkForm <> form_Dlink) then begin
  1084.     strcat(Command, " MAP ");
  1085.     if strlen(Map_name) > 0 then
  1086.         strcat(Command, Map_name)
  1087.     else begin
  1088.         strcat(Command, Dest_path);
  1089.         strcat(Command, BaseName);
  1090.         strcat(Command, ".MAP");
  1091.     end;
  1092.     if LinkForm = form_Blink then
  1093.         CatParam(Command, MapOptions);
  1094.     end;
  1095.  
  1096.     if opt_Xref and (LinkForm <> form_Dlink) then begin
  1097.     strcat(Command, " XREF ");
  1098.     if strlen(Xref_name) > 0 then
  1099.         strcat(Command, Xref_name)
  1100.     else begin
  1101.         strcat(Command, Dest_path);
  1102.         strcat(Command, BaseName);
  1103.         strcat(Command, ".XREF");
  1104.     end;
  1105.     end;
  1106.  
  1107.     if opt_SmallCode then begin
  1108.     if LinkForm = form_Alink then
  1109.         strcat(Command, " SMALL")
  1110.     else if LinkForm = form_Blink then
  1111.         strcat(Command, " SMALLCODE");
  1112.     end else if LinkForm = form_Dlink then
  1113.     strcat(Command, " -f");
  1114.  
  1115.     if opt_SmallData then begin
  1116.     if LinkForm = form_Blink then
  1117.         strcat(Command, " SMALLDATA")
  1118.     else if (LinkForm = form_Alink) and (not opt_SmallCode) then
  1119.         strcat(Command, " SMALL");
  1120.     end else if (LinkForm = form_Dlink) and opt_SmallCode then
  1121.     strcat(Command, " -f");
  1122.  
  1123.     if opt_Verbose then
  1124.     Writeln(Command);
  1125.  
  1126.     ExecuteProgram(Command);
  1127.  
  1128.     DOSClose(Handle);
  1129.     Handle := Nil;
  1130.  
  1131.     if LinkForm = form_Blink then begin
  1132.     if ReOpen(ErrorFile, Errors) then begin
  1133.         while not EOF(Errors) do begin
  1134.         ReadLn(Errors, Command);
  1135.         if strnieq(Command, "Error", 5) then begin
  1136.             Close(Errors);
  1137.             ProcessError(LinkError_name,FromName,ToName,ErrorFile);
  1138.         end;
  1139.         end;
  1140.         Close(Errors);
  1141.     end;
  1142.     end else if LinkForm = form_Alink then begin
  1143.     if ReOpen(ErrorFile, Errors) then begin
  1144.         while not EOF(Errors) do begin
  1145.         ReadLn(Errors, Command);
  1146.         if strnieq(Command, "Linker", 6) then begin
  1147.             Close(Errors);
  1148.             ProcessError(LinkError_name,FromName,ToName,ErrorFile);
  1149.         end;
  1150.         end;
  1151.         Close(Errors);
  1152.     end;
  1153.     end else if FileSize(ErrorFile) > 0 then
  1154.     ProcessError(LinkError_name,FromName,ToName,ErrorFile);
  1155.  
  1156.     if not DeleteFile(ErrorFile) then
  1157.     Abort("Could not delete temporary file");
  1158.  
  1159.     FreeString(Command);
  1160.     FreeString(ErrorFile);
  1161. end;
  1162.  
  1163. Procedure MultiLink(MainName : String; ExternalName : WordListPtr;
  1164.             BaseName : String; ToName : String);
  1165. var
  1166.     Command   : String;
  1167.     ErrorFile : String;
  1168.     LinkListName  : String;
  1169.     LinkList  : Text;
  1170.     Lib          : WordListPtr;
  1171.     Errors    : Text;
  1172. begin
  1173.     Command   := AllocString(256);
  1174.     ErrorFile := AllocString(256);
  1175.     LinkListName  := AllocString(256);
  1176.  
  1177.     GetTempFileName(ErrorFile, "");
  1178.  
  1179.     Handle := DOSOpen(ErrorFile, MODE_NEWFILE);
  1180.     if Handle = Nil then
  1181.     Abort("Could not open temporary file");
  1182.  
  1183.     GetTempFileName(LinkListName, "");
  1184.     if not Open(LinkListName, LinkList) then
  1185.     Abort("Could not open temporary file");
  1186.  
  1187.     strcpy(Command, Linker_path);
  1188.     if LinkForm <> form_Dlink then
  1189.     strcat(Command, " WITH ")
  1190.     else
  1191.     strcat(Command, " @");
  1192.     strcat(Command, LinkListName);
  1193.  
  1194.     if LinkForm <> form_Dlink then
  1195.     Write(LinkList, 'FROM ');
  1196.     Write(LinkList, MainName);
  1197.     if LinkForm = form_Dlink then
  1198.     Writeln(LinkList);
  1199.  
  1200.     while ExternalName <> Nil do begin
  1201.     if LinkForm <> form_Dlink then
  1202.         Write(LinkList, ',');
  1203.     Write(LinkList, ExternalName^.Name);
  1204.     if LinkForm = form_Dlink then
  1205.         Writeln(LinkList);
  1206.     ExternalName := ExternalName^.Next;
  1207.     end;
  1208.     if LinkForm <> form_Dlink then
  1209.     Writeln(LinkList);
  1210.  
  1211.     if LinkForm <> form_Dlink then
  1212.     strcat(Command, " TO ")
  1213.     else
  1214.     strcat(Command, " -o ");
  1215.     strcat(Command, ToName);
  1216.  
  1217.     Lib := LibraryList;
  1218.     if (Lib <> Nil) and (LinkForm <> form_Dlink) then
  1219.     Write(LinkList, 'LIBRARY ');
  1220.  
  1221.     while Lib <> Nil do begin
  1222.     Write(LinkList, Lib^.Name);
  1223.     Lib := Lib^.Next;
  1224.     if LinkForm = form_Dlink then
  1225.         Writeln(LinkList)
  1226.     else if Lib <> Nil then
  1227.         Write(LinkList, ',');
  1228.     end;
  1229.     if (LibraryList <> Nil) and (LinkForm <> form_Dlink) then
  1230.     Writeln(LinkList);
  1231.  
  1232.     if opt_NoDebug then begin
  1233.     if LinkForm = form_Blink then
  1234.         strcat(Command, " NODEBUG");
  1235.     end else if LinkForm = form_Dlink then
  1236.     strcat(Command, " -s");
  1237.  
  1238.     if opt_Map and (LinkForm <> form_Dlink) then begin
  1239.     strcat(Command, " MAP ");
  1240.     if strlen(Map_name) > 0 then
  1241.         strcat(Command, Map_name)
  1242.     else begin
  1243.         strcat(Command, Dest_path);
  1244.         strcat(Command, BaseName);
  1245.         strcat(Command, ".MAP");
  1246.     end;
  1247.     if LinkForm = form_Blink then
  1248.         CatParam(Command, MapOptions);
  1249.     end;
  1250.  
  1251.     if opt_Xref and (LinkForm <> form_Dlink) then begin
  1252.     strcat(Command," XREF ");
  1253.     if strlen(Xref_name) > 0 then
  1254.         strcat(Command, Xref_name)
  1255.     else begin
  1256.         strcat(Command, Dest_path);
  1257.         strcat(Command, BaseName);
  1258.         strcat(Command, ".XREF");
  1259.     end;
  1260.     end;
  1261.  
  1262.     if opt_SmallCode then begin
  1263.     if LinkForm = form_Blink then
  1264.         strcat(Command, " SMALLCODE")
  1265.     else if LinkForm = form_Alink then
  1266.         strcat(Command, " SMALL");
  1267.     end else if LinkForm = form_Dlink then
  1268.     strcat(Command, " -f");
  1269.  
  1270.     if opt_SmallData then begin
  1271.     if LinkForm = form_Blink then
  1272.         strcat(Command, " SMALLDATA")
  1273.     else if (LinkForm = form_Alink) and (not opt_SmallCode) then
  1274.         strcat(Command, " SMALL");
  1275.     end else if (LinkForm = form_Dlink) and opt_SmallCode then
  1276.     strcat(Command, " -f");
  1277.  
  1278.     Close(LinkList);
  1279.  
  1280.     if opt_Verbose then
  1281.     Writeln(Command);
  1282.  
  1283.     ExecuteProgram(Command);
  1284.  
  1285.     DOSClose(Handle);
  1286.     Handle := Nil;
  1287.  
  1288.     if LinkForm = form_Blink then begin
  1289.     if ReOpen(ErrorFile, Errors) then begin
  1290.         while not EOF(Errors) do begin
  1291.         ReadLn(Errors, Command);
  1292.         if strnieq(Command, "Error", 5) then begin
  1293.             Close(Errors);
  1294.             ProcessError(LinkError_name,MainName,ToName,ErrorFile);
  1295.         end;
  1296.         end;
  1297.         Close(Errors);
  1298.     end;
  1299.     end else if LinkForm = form_Alink then begin
  1300.     if ReOpen(ErrorFile, Errors) then begin
  1301.         while not EOF(Errors) do begin
  1302.         Readln(Errors, Command);
  1303.         if strnieq(Command, "Linker", 6) then begin
  1304.             Close(Errors);
  1305.             ProcessError(LinkError_name,MainName,ToName,ErrorFile);
  1306.         end;
  1307.         end;
  1308.         Close(Errors);
  1309.     end;
  1310.     end else if FileSize(ErrorFile) > 0 then
  1311.     ProcessError(LinkError_name,MainName,ToName,ErrorFile);
  1312.  
  1313.     if not DeleteFile(ErrorFile) then
  1314.     Abort("Could not delete temporary file");
  1315.     if not DeleteFile(LinkListName) then
  1316.     Abort("Could not delete temporary file");
  1317.  
  1318.     FreeString(LinkListName);
  1319.     FreeString(Command);
  1320.     FreeString(ErrorFile);
  1321. end;
  1322.  
  1323.  
  1324.  
  1325.     Procedure ProcessOption(OptionLine : String);
  1326.         Forward;
  1327.  
  1328.  
  1329. Procedure ProcessProject(ProjectName : String);
  1330. var
  1331.     ProjectFile : Text;
  1332.     ProjectLine : String;
  1333.     MainSuffix  : WordListPtr;
  1334.     BaseName    : String;
  1335. begin
  1336.     SetList(External_names, "");
  1337.  
  1338.     if strlen(Main_name) > 0 then begin
  1339.     FreeString(Main_name);
  1340.     Main_name := strdup("");
  1341.     end;
  1342.  
  1343.     if strlen(Exec_name) > 0 then begin
  1344.     FreeString(Exec_name);
  1345.     Exec_name := strdup("");
  1346.     end;
  1347.  
  1348.     ProjectLine := AllocString(256);
  1349.     if reopen(ProjectName, ProjectFile) then begin
  1350.     while not EOF(ProjectFile) do begin
  1351.         CommandSource := src_project;
  1352.         Readln(ProjectFile, ProjectLine);
  1353.         ProcessOption(ProjectLine);
  1354.     end;
  1355.     Close(ProjectFile);
  1356.     end;
  1357.  
  1358.     if strlen(Main_name) = 0 then
  1359.     Abort("No MAIN module was specified in the project file");
  1360.  
  1361.     BaseName := AllocString(256);
  1362.  
  1363.     if strlen(Exec_name) = 0 then begin
  1364.     OnlyFileName(Main_name,BaseName);
  1365.     MainSuffix := HasSuffix(BaseName, Object_suffix);
  1366.     if MainSuffix <> Nil then
  1367.         StripSuffix(BaseName, MainSuffix^.Name);
  1368.     strcpy(ProjectLine, Dest_path);
  1369.     strcat(ProjectLine, BaseName);
  1370.     SetName(Exec_name, ProjectLine);
  1371.     end else begin
  1372.     OnlyFileName(Exec_name, BaseName);
  1373.     if (strpos(Exec_name,':') = -1) and
  1374.        (strpos(Exec_name,'/') = -1) then begin
  1375.         strcpy(ProjectLine, Dest_path);
  1376.         strcat(ProjectLine, Exec_name);
  1377.         SetName(Exec_name, ProjectLine);
  1378.     end;
  1379.     end;
  1380.  
  1381.     MultiLink(Main_name, External_names, BaseName, Exec_name);
  1382.  
  1383.     FreeString(ProjectLine);
  1384. end;
  1385.  
  1386. Procedure ProcessPascal(SourceName : String);
  1387. var
  1388.     Suffix   : WordListPtr;
  1389.     BaseName : String;
  1390.     FromName,
  1391.     ToName   : String;
  1392.     BaseBuffer,
  1393.     FromBuffer,
  1394.     SourceBuffer,
  1395.     ToBuffer : Array [0..255] of Char;
  1396. begin
  1397.     BaseName := String(@BaseBuffer);
  1398.     FromName := String(@FromBuffer);
  1399.     ToName   := String(@ToBuffer);
  1400.  
  1401.     Suffix := HasSuffix(SourceName, Pascal_suffix);
  1402.  
  1403.     OnlyFileName(SourceName, BaseName);
  1404.     if Suffix <> Nil then
  1405.     StripSuffix(BaseName, Suffix^.Name);
  1406.  
  1407.     if not ExistFile(SourceName) then begin
  1408.     if (strpos(SourceName, ':') = -1) and
  1409.        (strpos(SourceName, '/') = -1) then begin
  1410.         strcpy(FromName, Source_path);
  1411.         strcat(FromName, SourceName);
  1412.         SourceName := String(@SourceBuffer);
  1413.         strcpy(SourceName, FromName);
  1414.     end;
  1415.     if not ExistFile(SourceName) then
  1416.         Abort("Could not find source file");
  1417.     end;
  1418.  
  1419.     if (CommandSource <> src_CommandLine) and (not opt_Force) then begin
  1420.     strcpy(ToName, Dest_path);
  1421.     strcat(ToName, BaseName);
  1422.     if Object_suffix <> Nil then
  1423.         strcat(ToName, Object_suffix^.Name);
  1424.  
  1425.     if ExistFile(ToName) then begin
  1426.         if FileTime(ToName) > FileTime(SourceName) then begin
  1427.         if opt_Verbose then
  1428.             Writeln('Skipping ', SourceName, ' (no changes)');
  1429.         if CommandSource = src_project then begin
  1430.             if FileClass = class_main then
  1431.             SetName(Main_name, ToName)
  1432.             else if FileClass = class_external then
  1433.             AddWord(External_names, ToName);
  1434.         end;
  1435.         return;
  1436.         end;
  1437.     end;
  1438.     end;
  1439.  
  1440.     Compile(SourceName, BaseName, ToName);
  1441.  
  1442.     if opt_Optimize then begin
  1443.     strcpy(FromName, ToName);
  1444.     Optimize(FromName, BaseName, ToName);
  1445.     if not DeleteFile(FromName) then
  1446.         Abort("Could not delete temporary file");
  1447.     end;
  1448.  
  1449.     if opt_Assemble then begin
  1450.     strcpy(FromName, ToName);
  1451.     Assemble(FromName, BaseName, ToName);
  1452.     if not DeleteFile(FromName) then
  1453.         Abort("Could not delete temporary file");
  1454.     end else
  1455.     return;
  1456.  
  1457.     if opt_Link and (CommandSource = src_CommandLine) then begin
  1458.     strcpy(FromName, ToName);
  1459.     Link(FromName, BaseName, ToName);
  1460.     if not DeleteFile(FromName) then
  1461.         Abort("Could not delete temporary file");
  1462.     end;
  1463.  
  1464.     if CommandSource = src_Project then begin
  1465.     if FileClass = class_main then
  1466.         SetName(Main_name, ToName)
  1467.     else if FileClass = class_external then
  1468.         AddWord(External_names, ToName);
  1469.     end;
  1470. end;
  1471.  
  1472.  
  1473. Procedure ProcessAssembly(SourceName : String);
  1474. var
  1475.     Suffix   : WordListPtr;
  1476.     BaseName : String;
  1477.     FromName,
  1478.     ToName   : String;
  1479.     BaseBuffer,
  1480.     FromBuffer,
  1481.     SourceBuffer,
  1482.     ToBuffer : Array [0..255] of Char;
  1483. begin
  1484.     BaseName := String(@BaseBuffer);
  1485.     FromName := String(@FromBuffer);
  1486.     ToName   := String(@ToBuffer);
  1487.  
  1488.     Suffix := HasSuffix(SourceName, Assembly_suffix);
  1489.  
  1490.     OnlyFileName(SourceName, BaseName);
  1491.     if Suffix <> Nil then
  1492.     StripSuffix(BaseName, Suffix^.Name);
  1493.  
  1494.     if not ExistFile(SourceName) then begin
  1495.     if (strpos(SourceName, ':') = -1) and
  1496.        (strpos(SourceName, '/') = -1) then begin
  1497.         strcpy(FromName, Source_path);
  1498.         strcat(FromName, SourceName);
  1499.         SourceName := String(@SourceBuffer);
  1500.         strcpy(SourceName, FromName);
  1501.     end;
  1502.     if not ExistFile(SourceName) then
  1503.         Abort("Could not find source file");
  1504.     end;
  1505.  
  1506.     if (CommandSource <> src_CommandLine) and (not opt_Force) then begin
  1507.     strcpy(ToName, Dest_path);
  1508.     strcat(ToName, BaseName);
  1509.     if Object_suffix <> Nil then
  1510.         strcat(ToName, Object_suffix^.Name);
  1511.  
  1512.     if ExistFile(ToName) then begin
  1513.         if FileTime(ToName) > FileTime(SourceName) then begin
  1514.         if opt_Verbose then
  1515.             Writeln('Skipping ', SourceName, ' (no changes)');
  1516.         if CommandSource = src_project then begin
  1517.             if FileClass = class_main then
  1518.             SetName(Main_name, ToName)
  1519.             else if FileClass = class_external then
  1520.             AddWord(External_names, ToName);
  1521.         end;
  1522.         return;
  1523.         end;
  1524.     end;
  1525.     end;
  1526.  
  1527.     Assemble(SourceName, BaseName, ToName);
  1528.  
  1529.     if opt_Link and (CommandSource = src_CommandLine) then begin
  1530.     strcpy(FromName, ToName);
  1531.     Link(FromName, BaseName, ToName);
  1532.     if not DeleteFile(FromName) then
  1533.         Abort("Could not delete temporary file");
  1534.     end;
  1535.  
  1536.     if CommandSource = src_Project then begin
  1537.     if FileClass = class_main then
  1538.         SetName(Main_name, ToName)
  1539.     else if FileClass = class_external then
  1540.         AddWord(External_names, ToName);
  1541.     end;
  1542. end;
  1543.  
  1544.  
  1545. Procedure ProcessObject(ObjectName : String);
  1546. var
  1547.     Suffix   : WordListPtr;
  1548.     BaseName : String;
  1549.     ToName   : String;
  1550.     BaseBuffer,
  1551.     SourceBuffer,
  1552.     ToBuffer : Array [0..255] of Char;
  1553. begin
  1554.     BaseName := String(@BaseBuffer);
  1555.     ToName   := String(@ToBuffer);
  1556.  
  1557.     Suffix := HasSuffix(ObjectName, Object_suffix);
  1558.  
  1559.     OnlyFileName(ObjectName, BaseName);
  1560.     if Suffix <> Nil then
  1561.     StripSuffix(BaseName, Suffix^.Name);
  1562.  
  1563.     if not ExistFile(ObjectName) then begin
  1564.     if (strpos(ObjectName, ':') = -1) and
  1565.        (strpos(ObjectName, '/') = -1) then begin
  1566.         strcpy(ToName, Source_path);
  1567.         strcat(ToName, ObjectName);
  1568.         ObjectName := String(@SourceBuffer);
  1569.         strcpy(ObjectName, ToName);
  1570.     end;
  1571.     if not ExistFile(ObjectName) then
  1572.         Abort("Could not find source file");
  1573.     end;
  1574.  
  1575.     if CommandSource = src_Project then begin
  1576.     if FileClass = class_main then
  1577.         SetName(Main_name, ObjectName)
  1578.     else if FileClass = class_external then
  1579.         AddWord(External_names, ObjectName);
  1580.     end else if CommandSource = src_CommandLine then
  1581.     Link(ObjectName, BaseName, ToName);
  1582. end;
  1583.  
  1584.  
  1585. Procedure ProcessSource(Param : String);
  1586. var
  1587.     FileName : String;
  1588.     Suffix   : WordListPtr;
  1589. begin
  1590.     if HasSuffix(Param, Project_suffix) <> Nil then
  1591.     ProcessProject(Param)
  1592.     else if HasSuffix(Param, Pascal_suffix) <> Nil then
  1593.     ProcessPascal(Param)
  1594.     else if HasSuffix(Param, Assembly_suffix) <> Nil then
  1595.     ProcessAssembly(Param)
  1596.     else if HasSuffix(Param, Object_suffix) <> Nil then
  1597.     ProcessObject(Param)
  1598.     else if CommandSource <> src_CommandLine then
  1599.     Abort("Unknown parameter")
  1600.     else begin
  1601.  
  1602.     FileName := AllocString(256);
  1603.  
  1604.     Suffix := Project_suffix;
  1605.     while Suffix <> Nil do begin
  1606.         strcpy(FileName, Param);
  1607.         strcat(FileName, Suffix^.Name);
  1608.         if ExistFile(FileName) then begin
  1609.         ProcessProject(FileName);
  1610.         return;
  1611.         end;
  1612.         Suffix := Suffix^.Next;
  1613.     end;
  1614.  
  1615.     Suffix := Pascal_suffix;
  1616.     while Suffix <> Nil do begin
  1617.         strcpy(FileName, Param);
  1618.         strcat(FileName, Suffix^.Name);
  1619.         if ExistFile(FileName) then begin
  1620.         ProcessPascal(FileName);
  1621.         return;
  1622.         end;
  1623.         Suffix := Suffix^.Next;
  1624.     end;
  1625.  
  1626.     Suffix := Assembly_suffix;
  1627.     while Suffix <> Nil do begin
  1628.         strcpy(FileName, Param);
  1629.         strcat(FileName, Suffix^.Name);
  1630.         if ExistFile(FileName) then begin
  1631.         ProcessAssembly(FileName);
  1632.         return;
  1633.         end;
  1634.         Suffix := Suffix^.Next;
  1635.     end;
  1636.  
  1637.     Suffix := Object_suffix;
  1638.     while Suffix <> Nil do begin
  1639.         strcpy(FileName, Param);
  1640.         strcat(FileName, Suffix^.Name);
  1641.         if ExistFile(FileName) then begin
  1642.         ProcessObject(FileName);
  1643.         return;
  1644.         end;
  1645.         Suffix := Suffix^.Next;
  1646.     end;
  1647.  
  1648.     Abort("Unknown parameter");
  1649.  
  1650.     FreeString(FileName);
  1651.     end;
  1652. end;
  1653.  
  1654. Procedure ProcessMain(MainName : String);
  1655. begin
  1656.     if CommandSource <> src_project then
  1657.     Abort("MAIN only allowed in project files");
  1658.     if strlen(Main_name) > 0 then
  1659.     Abort("Only one MAIN file allowed per project");
  1660.  
  1661.     FileClass := class_main;
  1662.     ProcessSource(MainName);
  1663.     FileClass := class_unknown;
  1664. end;
  1665.  
  1666. Procedure ProcessExternal(FileName : String);
  1667. begin
  1668.     if CommandSource <> src_project then
  1669.     Abort("EXTERNAL only allowed in project files");
  1670.  
  1671.     FileClass := class_external;
  1672.     ProcessSource(FileName);
  1673.     FileClass := class_unknown;
  1674. end;
  1675.  
  1676. Procedure SetExecName(name : String);
  1677. begin
  1678.     if strlen(Exec_name) > 0 then
  1679.     Abort("Only one EXEC command allowed per project");
  1680.     SetName(Exec_name, name);
  1681. end;
  1682.  
  1683. Procedure SetLinkForm(form : String);
  1684. begin
  1685.     case toupper(Form^) of
  1686.       'A' : LinkForm := form_Alink;
  1687.       'B' : LinkForm := form_Blink;
  1688.       'D' : LinkForm := form_Dlink;
  1689.     else
  1690.     Abort("Unknown linker format");
  1691.     end;
  1692. end;
  1693.  
  1694. Procedure ProcessOption(OptionLine : String);
  1695. var
  1696.     Prefix  : Char;
  1697.     Command : String;
  1698.     Suffix  : String;
  1699. begin
  1700.     if CheckBreak then
  1701.     Abort("User aborted");
  1702.  
  1703.     if (OptionLine^ = Chr(0)) or (OptionLine^ = '*') then
  1704.     return;
  1705.  
  1706.     if (OptionLine[0] = '-') or (OptionLine[0] = '+') then begin
  1707.     Prefix := OptionLine[0];
  1708.     strdel(OptionLine,0,1);
  1709.     end else
  1710.     Prefix := ' ';
  1711.  
  1712.     SplitLine(OptionLine, Command, Suffix);
  1713.  
  1714.     case FindOption(Command) of
  1715.     key_a,
  1716.     key_assemble    : SetSwitch(opt_assemble, Prefix);
  1717.     key_assembler    : SetCommandPath(Assembler_Path, Suffix);
  1718.     key_assemblererror
  1719.             : SetName(AssemError_name, Suffix);
  1720.     key_assembly    : SetList(Assembly_suffix, Suffix);
  1721.     key_b,
  1722.     key_shortcircuit : SetSwitch(opt_shortcircuit, Prefix);
  1723.     key_c,
  1724.     key_ccalls    : SetSwitch(opt_CCalls, Prefix);
  1725.     key_i,
  1726.     key_checkio    : SetSwitch(opt_CheckIO, Prefix);
  1727.     key_compiler    : SetCommandPath(Compiler_path, Suffix);
  1728.     key_compilererror
  1729.             : SetName(CompError_name, Suffix);
  1730.     key_d,
  1731.     key_debug    : SetSwitch(opt_Debug, Prefix);
  1732.     key_destination : SetPath(Dest_Path, Suffix);
  1733.     key_x,
  1734.     key_discard    : SetSwitch(opt_Discard, Prefix);
  1735.     key_exec    : SetExecName(Suffix);
  1736.     key_execute    : ExecuteProgram(Suffix);
  1737.     key_external    : ProcessExternal(Suffix);
  1738.     key_f,
  1739.     key_force    : SetSwitch(opt_Force, Prefix);
  1740.     key_l,
  1741.     key_link    : SetSwitch(opt_Link, Prefix);
  1742.     key_library    : SetList(LibraryList, Suffix);
  1743.     key_linker    : SetCommandPath(Linker_path, Suffix);
  1744.     key_linkererror    : SetName(LinkError_name, Suffix);
  1745.     key_linkform    : SetLinkForm(Suffix);
  1746.     key_main    : ProcessMain(Suffix);
  1747.     key_map        : SetMapName(Prefix, Suffix);
  1748.     key_mapoptions    : SetName(MapOptions, Suffix);
  1749.     key_mathtrans,
  1750.     key_n        : SetSwitch(opt_MathTrans, Prefix);
  1751.     key_nodebug    : SetSwitch(opt_NoDebug, Prefix);
  1752.     key_o,
  1753.     key_optimize    : SetSwitch(opt_optimize, Prefix);
  1754.     key_object    : SetList(Object_suffix, Suffix);
  1755.     key_optimizer    : SetCommandPath(Optimizer_path, Suffix);
  1756.     key_optimizererror
  1757.             : SetName(OptError_name, Suffix);
  1758.     key_p,
  1759.     key_profiler    : SetSwitch(opt_Profiler, Prefix);
  1760.     key_pascal    : SetList(Pascal_suffix, Suffix);
  1761.     key_project    : SetList(Project_suffix, Suffix);
  1762.     key_r,
  1763.     key_range    : SetSwitch(opt_RangeCheck, Prefix);
  1764.     key_smallcode    : SetSwitch(opt_SmallCode, Prefix);
  1765.     key_smalldata    : SetSwitch(opt_SmallData, Prefix);
  1766.     key_s,
  1767.     key_smallinit   : SetSwitch(opt_SmallInit, Prefix);
  1768.     key_source    : SetPath(Source_Path, Suffix);
  1769.     key_temp    : SetPath(Temp_Path, Suffix);
  1770.     key_v,
  1771.     key_verbose    : SetSwitch(opt_Verbose, Prefix);
  1772.     key_xref    : SetXrefName(Prefix,Suffix);
  1773.     else begin
  1774.          if (Prefix = ' ') and (strlen(Suffix) = 0) then
  1775.          ProcessSource(Command)
  1776.          else
  1777.          Writeln('Unknown command: ', Command);
  1778.      end;
  1779.     end;
  1780. end;
  1781.  
  1782.  
  1783. Procedure ReadCfg(FileName : String);
  1784. var
  1785.     CfgFile : Text;
  1786.     CfgLine : String;
  1787. begin
  1788.     CfgLine := AllocString(256);
  1789.     if reopen(FileName, CfgFile) then begin
  1790.     while not EOF(CfgFile) do begin
  1791.         CommandSource := src_cfg;
  1792.         Readln(CfgFile, CfgLine);
  1793.         ProcessOption(CfgLine);
  1794.     end;
  1795.     Close(CfgFile);
  1796.     end;
  1797.     FreeString(CfgLine);
  1798. end;
  1799.  
  1800. var
  1801.     Param    : String;
  1802.     ParamNum : Integer;    
  1803. begin
  1804.     Handle := Nil;
  1805.  
  1806.     SetDefaults;
  1807.  
  1808.     if ExistFile("s:pcq.cfg") then
  1809.     ReadCfg("s:pcq.cfg");
  1810.     if ExistFile("pcq.cfg") then
  1811.     ReadCfg("pcq.cfg");
  1812.  
  1813.     ParamNum := 1;
  1814.     Param := AllocString(256);
  1815.     repeat
  1816.     GetParam(ParamNum,Param);
  1817.     Inc(ParamNum);
  1818.  
  1819.     CommandSource := src_CommandLine;
  1820.  
  1821.     if Param^ = Chr(0) then
  1822.         { Nothing }
  1823.     else if (Param^ = '-') or (Param^ = '+') then
  1824.         ProcessOption(Param)
  1825.     else
  1826.         ProcessSource(Param);
  1827.     until Param^ = Chr(0);
  1828.     if ParamNum = 2 then begin
  1829.     Writeln('PCQ Make Utility version 1.02 (September 18, 1991)');
  1830.     Writeln('Usage: PCQ <options> <files> ...');
  1831.     Writeln('       where <options> include all the configuration commands');
  1832.     Writeln('       and <files> include project, Pascal, assembly or object files');
  1833.     end;
  1834. end.
  1835.  
  1836.